home *** CD-ROM | disk | FTP | other *** search
/ Nautilus 1992 July / Nautilus-3-8 / Nautilus-3-8.bin / Tools & Utilities / Techy Stuff / Source ƒ / SFGetFolder ƒ / StandardGetFolder.c < prev   
Encoding:
C/C++ Source or Header  |  1992-06-06  |  20.7 KB  |  417 lines

  1. /*******************************************************************************
  2. * StandardGetFolder.c                                                          *
  3. *                                                                              *
  4. *    This little chunk o' code implements a way to let the user choose a       *
  5. *    folder to save files in via a StandardFile Dialog.                        *
  6. *                                                                              *
  7. *    Since the code uses the CustomGetFile function and depends on the use of  *
  8. *    FSSpec records, it only works under System 7.0 or later.                  *                        
  9. *                                                                              *
  10. *    And don't forget to include the custom dialog resources ( a 'DITL' and    *
  11. *   'DLOG') in your project.                                                   *                                                                   *
  12. *                                                                              *
  13. *    Portions of this code were originally provided by Paul Forrester          *
  14. *    (paulf@apple.com) to the think-c internet mailing list in response to my  *
  15. *    my question on how to do exactly what this code does.  I've added a       *
  16. *    couple of features, such as the ability to handle aliased folders and     *
  17. *    the programmer definable prompt.  I also cleaned and tightened the code,  *
  18. *    stomped a couple of bugs, and packaged it up neatly.  Bunches of work,    *
  19. *    but I learned A LOT about Standard File, the File Manager, the Dialog     *
  20. *    Manager, and the Alias Manager.  I tried to include in the comments some  *
  21. *    of the neat stuff I discovered in my hours of pouring over Inside Mac.    *
  22. *    Hope you find it educational as well as useful.                           *
  23. *******************************************************************************/
  24.  
  25. #include <stdio.h>
  26. #include <String.h>
  27. #include <Script.h>
  28. #include <Aliases.h>
  29.  
  30. /*=============================================================================+
  31. |                            Function Prototypes                               |
  32. +=============================================================================*/
  33. long GetSFCurDir(void);
  34.  
  35. short GetSFVRefNum(void);
  36.  
  37. pascal Boolean MyCustomGetDirectoryFileFilter(  CInfoPBPtr  myPB, 
  38.                                                 Ptr         myDataPtr);
  39.  
  40. void SetButtonTitle ( Handle    ButtonHdl, 
  41.                       Str255    name, 
  42.                       Rect      *ButtonRect);
  43.  
  44. pascal short MyCustomGetDirectoryDlogHook( short        item, 
  45.                                            DialogPtr    theDialog,
  46.                                            Ptr          myDataPtr);
  47.  
  48. char *StrCpy (char *s1, char *s2);
  49.  
  50. char *StrCat (char *s1, char *s2);                                      
  51.  
  52.  
  53. /*=============================================================================+
  54. |                               Resource IDs                                   |
  55. +=============================================================================*/
  56. #define rGetFolderButton            10
  57. #define rGetFolderMessage           11
  58. #define kFolderBit                  0x0010  
  59. #define rGetFolderDialog            2008
  60.  
  61.  
  62. /*=============================================================================+
  63. |                             Global Variables                                 |
  64. +=============================================================================*/
  65. static  char    gCurrentSelectedFolder [256];
  66.  
  67.  
  68. /*******************************************************************************
  69. * StandardGetFolder                                                            *
  70. *                                                                              *
  71. *     The StandardGetFolder function. You pass it the point where you want the *
  72. *     standard file dialog box drawn, the prompt to display above the file     *
  73. *     list, and a pointer to an StandardFileReply record.                      *
  74. *                                                                              *
  75. *     Upon return, the sfFile field of the SFReply record contains the volume  *
  76. *     reference number and directory ID that specify the folder the user       *
  77. *     chose. It also passes back the name of the chosen folder.  The sfGood    *
  78. *     field is set to true if the user chose a folder, or false if not.        *
  79. *******************************************************************************/
  80.  
  81. void StandardGetFolder (    Point               where,
  82.                             Str255              message,
  83.                             StandardFileReply   *mySFReply)
  84.                             
  85. {
  86.     SFTypeList              theTypeList;
  87.     short                   numTypes;
  88.     ProcPtr                 myModalFilter;
  89.     CInfoPBRec              pb;
  90.     OSErr                   err;
  91.     short                   theItem;
  92.  
  93.     /*-------------------------------------------------------------------------+
  94.     | Setting num types to -1 tells CustomGetFile to pass all files and        |
  95.     | folders to the file filter function.                                     |
  96.     +-------------------------------------------------------------------------*/
  97.     numTypes = - 1;         
  98.  
  99.     /*-------------------------------------------------------------------------+
  100.     | Copy the prompt to be displayed above the file list into the name field  |
  101.     | of the SFReply record. When MyCustomGetDirectoryDlogHook is called for   |
  102.     | the first time, it will use this info to draw the prompt.                |
  103.     +-------------------------------------------------------------------------*/
  104.     StrCpy ( (char *) mySFReply->sfFile.name, (char *) message);
  105.     
  106.     /*-------------------------------------------------------------------------+
  107.     | Call CustomGetFile. Pass it a pointer to the file filter and dialog      |  
  108.     | hook functions. Also pass a pointer to mySFReply in the user data field. |
  109.     +-------------------------------------------------------------------------*/
  110.     CustomGetFile(  (ProcPtr)MyCustomGetDirectoryFileFilter, 
  111.                     numTypes,
  112.                     theTypeList,
  113.                     mySFReply, 
  114.                     rGetFolderDialog, 
  115.                     where,
  116.                     (ProcPtr)MyCustomGetDirectoryDlogHook, 
  117.                     NULL, 
  118.                     (short*)NULL,
  119.                     NULL, 
  120.                     (void *)(mySFReply));
  121.  
  122.     /*-------------------------------------------------------------------------+
  123.     | Ok, now the reply record contains the volume reference number and the    |
  124.     | name of the selected folder. We need to use PBGetCatInfo to get the      |    
  125.     | directory ID of the selected folder.                                     |
  126.     +-------------------------------------------------------------------------*/
  127.     pb.hFileInfo.ioCompletion = NULL;
  128.     pb.hFileInfo.ioNamePtr = mySFReply->sfFile.name;
  129.     pb.hFileInfo.ioVRefNum = mySFReply->sfFile.vRefNum;
  130.     pb.hFileInfo.ioFDirIndex = 0;
  131.     pb.hFileInfo.ioDirID = mySFReply->sfFile.parID;
  132.  
  133.     err = PBGetCatInfo( &pb, FALSE);
  134.     
  135.     /*-------------------------------------------------------------------------+
  136.     | Insert your error handler here. I couldn't think of one so I left it     |
  137.     | empty. Works fine without it.                                            |
  138.     +-------------------------------------------------------------------------*/
  139.     if (  err != noErr);
  140.     
  141.     /*-------------------------------------------------------------------------+
  142.     | Copy the directory ID of the selected folder to the sfFile field of the  |
  143.     | SFReply record.                                                          |
  144.     +-------------------------------------------------------------------------*/
  145.     mySFReply->sfFile.parID = pb.dirInfo.ioDrDirID;
  146.  
  147. }
  148.  
  149.  
  150.  
  151. /*******************************************************************************
  152. * MyCustomGetDirectoryFileFilter                                               *
  153. *                                                                              *
  154. *     This is the file filter passed to CustomGetFile. It passes folders only. *
  155. *******************************************************************************/
  156. pascal Boolean
  157. MyCustomGetDirectoryFileFilter( CInfoPBPtr  myPB, 
  158.                                 Ptr         myDataPtr )
  159. {
  160.     return( ! (myPB->hFileInfo.ioFlAttrib & kFolderBit ) );
  161. }
  162.  
  163.  
  164. /*******************************************************************************
  165. * MyCustomGetDirectoryDlogHook                                                 *
  166. *                                                                              *
  167. *     This function lets us process item hits in the GetFolderDialog.  We're   *
  168. *     only interested if the user hit the selectFolder button. We pass all     *
  169. *     other item hits back to ModalDialog.                                     *
  170. *******************************************************************************/
  171.  
  172. pascal short MyCustomGetDirectoryDlogHook(  short       item, 
  173.                                             DialogPtr   theDialog, 
  174.                                             Ptr         myDataPtr )
  175. {
  176.  
  177.     WindowPeek      dlgPeek;
  178.     Str255          selectedName;
  179.     CInfoPBRec      pb;
  180.     short           MyCustomGetDirectoryDlogHook;
  181.     OSErr           err;
  182.     int             itemType;           
  183.     Rect            itemRect;                                   
  184.     Handle          itemHandle;
  185.     Boolean         isAlias,
  186.                     isFolder;
  187.  
  188.     StandardFileReply           *mySFRPtr;
  189.  
  190.     
  191.     
  192.     /*-------------------------------------------------------------------------+
  193.     | Set the return value to defualt to the item that was passed in.          |
  194.     +-------------------------------------------------------------------------*/
  195.     MyCustomGetDirectoryDlogHook = item;
  196.  
  197.     /*-------------------------------------------------------------------------+
  198.     | CustomGet calls dialog hook for both main and subsidiary dialog boxes.   |
  199.     | Make sure that dialog record indicates that this is the main GetFolder   |
  200.     | dialog.                                                                  |
  201.     +-------------------------------------------------------------------------*/
  202.     dlgPeek = (WindowPeek)(theDialog);
  203.     if ( (OSType)(dlgPeek->refCon) == sfMainDialogRefCon )
  204.     {
  205.         /*---------------------------------------------------------------------+
  206.         | Get a handle to the select folder button, in case we need to change  |
  207.         | the label.                                                           |
  208.         +---------------------------------------------------------------------*/
  209.         GetDItem(theDialog, rGetFolderButton, &itemType, &itemHandle, &itemRect);
  210.  
  211.         /*---------------------------------------------------------------------+
  212.         | If this is the first time the dialog hook has been called...         |
  213.         +---------------------------------------------------------------------*/
  214.         if ( item == sfHookFirstCall )
  215.         {
  216.             /*-----------------------------------------------------------------+
  217.             | Set the prompt displayed above the file list...                  |
  218.             +-----------------------------------------------------------------*/
  219.             GetDItem ( theDialog, rGetFolderMessage, &itemType, &itemHandle, 
  220.                         &itemRect );
  221.             mySFRPtr = (StandardFileReply *)(myDataPtr);
  222.             SetIText ( itemHandle, mySFRPtr->sfFile.name );
  223.  
  224.             /*-----------------------------------------------------------------+
  225.             | And the name of the currently selected folder in the select      |
  226.             | folder button.                                                   |
  227.             +-----------------------------------------------------------------*/
  228.             pb.hFileInfo.ioCompletion = NULL;
  229.             pb.hFileInfo.ioNamePtr = (StringPtr)selectedName;
  230.             pb.hFileInfo.ioVRefNum = GetSFVRefNum();
  231.             pb.hFileInfo.ioDirID = GetSFCurDir();
  232.             pb.hFileInfo.ioFDirIndex = -1;
  233.             err = PBGetCatInfo( &pb, FALSE);
  234.  
  235.             /*-----------------------------------------------------------------+
  236.             | Note that this error return is important! When the dialog hook   |
  237.             | is called for the first time, Super Boomerang (and possibly      |
  238.             | Norton directory assistance aren't finished doing their          |
  239.             | rebounting, so the values returned by GetSFVRefNum and           |
  240.             | GetSFCurDir may not be valid, and hence PBGetCatInfo will return |
  241.             | an error.  That one took me a while to figure out.               |
  242.             +-----------------------------------------------------------------*/
  243.             if ( err != noErr )
  244.             {
  245.                 return (MyCustomGetDirectoryDlogHook);
  246.             }
  247.             
  248.             GetDItem(theDialog, rGetFolderButton, &itemType, &itemHandle, 
  249.                      &itemRect);
  250.             SetButtonTitle( itemHandle, selectedName, &itemRect);
  251.         }
  252.         
  253.         else
  254.         {
  255.             /*-----------------------------------------------------------------+
  256.             | Cast myDataPtr back to a SFReply pointer.                        |
  257.             +-----------------------------------------------------------------*/
  258.             mySFRPtr = (StandardFileReply *)( myDataPtr );
  259.  
  260.  
  261.             /*-----------------------------------------------------------------+
  262.             | If the selected folder is an alias, resolve it. isFolder will    |
  263.             | be set to true if a folder or aliased folder is selected.        |
  264.             +-----------------------------------------------------------------*/
  265.             ResolveAliasFile (&(mySFRPtr->sfFile), TRUE, &isFolder, &isAlias);
  266.             if ( (isAlias) && (isFolder) )
  267.                 StrCpy( (char *)selectedName, (char *)mySFRPtr->sfFile.name );
  268.                 
  269.             /*-----------------------------------------------------------------+
  270.             | If the selected item is a folder or volume, just copy the name   |
  271.             | into selectedName...                                             |
  272.             +-----------------------------------------------------------------*/
  273.             else if (( mySFRPtr->sfIsFolder) || (mySFRPtr->sfIsVolume) )
  274.                 StrCpy( (char *)selectedName, (char *)mySFRPtr->sfFile.name );
  275.  
  276.             /*-----------------------------------------------------------------+
  277.             | Otherwise, copy the name of the selected item's parent directory |
  278.             | into selectedName.                                               |
  279.             +-----------------------------------------------------------------*/
  280.             else
  281.             {
  282.                 pb.hFileInfo.ioCompletion = NULL;
  283.                 pb.hFileInfo.ioNamePtr = (StringPtr)selectedName;
  284.                 pb.hFileInfo.ioVRefNum = mySFRPtr->sfFile.vRefNum;
  285.                 pb.hFileInfo.ioDirID = mySFRPtr->sfFile.parID;
  286.                 pb.hFileInfo.ioFDirIndex = -1;
  287.                 err = PBGetCatInfo( &pb, FALSE);
  288.                 if ( err != noErr)
  289.                     return (MyCustomGetDirectoryDlogHook);
  290.             }
  291.             
  292.             /*-----------------------------------------------------------------+
  293.             | If the selected folder has changed since the last call to this   |
  294.             | dialog hook function, re-draw the button with the new selected   |
  295.             | folder name.                                                     |
  296.             +-----------------------------------------------------------------*/
  297.             if ( !EqualString( selectedName, (StringPtr)gCurrentSelectedFolder, 
  298.                                 FALSE, FALSE ) ) 
  299.                 SetButtonTitle(itemHandle, selectedName, &itemRect);
  300.  
  301.             /*-----------------------------------------------------------------+
  302.             | If the user clicked the select folder button, force a cancel and |
  303.             | set the sfGood field of the Reply record to true.                |
  304.             +-----------------------------------------------------------------*/
  305.             if (item == rGetFolderButton)
  306.             {
  307.                 MyCustomGetDirectoryDlogHook = sfItemCancelButton;
  308.                 mySFRPtr->sfGood = TRUE;
  309.             }
  310.                 
  311.         }
  312.     }
  313.     
  314.     return  (MyCustomGetDirectoryDlogHook );
  315. }
  316.  
  317.  
  318.  
  319. /*******************************************************************************
  320. * SetButtonTitle                                                               *
  321. *                                                                              *
  322. *     Whenever the selected folder is changed, SetButtonTitle is called to     *
  323. *     redraw the get folder button.  Pass it a handle to the button, the new   *
  324. *     string to be drawn in the button, and a pointer to the rect the button   *
  325. *     is drawn within.                                                         *
  326. *******************************************************************************/
  327. void SetButtonTitle(    Handle      ButtonHdl, 
  328.                         Str255      name, 
  329.                         Rect        *ButtonRect )
  330. {
  331.     short   resultCode;
  332.     short   width;
  333.     char    TmpStr[ 256 ];
  334.  
  335.     StrCpy( gCurrentSelectedFolder, (char*) name );
  336.     
  337.     /*-------------------------------------------------------------------------+
  338.     | Find the width left over in the button after drawing the word 'Select'   |
  339.     | the quotation marks. Truncate the new name to this length.               |
  340.     +-------------------------------------------------------------------------*/
  341.     width = (ButtonRect->right - ButtonRect->left) -
  342.             (StringWidth((StringPtr)"\pSelect \"\"") +
  343.              CharWidth('J'));
  344.     
  345.     resultCode = TruncString(width, name, smTruncEnd );
  346.     if ( resultCode < 0 );
  347.     
  348.     /*-------------------------------------------------------------------------+
  349.     | Redraw the button.                                                       |
  350.     +-------------------------------------------------------------------------*/
  351.     sprintf( TmpStr, "Select \"%#s\"", name );
  352.     CtoPstr( TmpStr );
  353.     SetCTitle((ControlHandle)(ButtonHdl), (StringPtr)TmpStr );
  354.     ValidRect(ButtonRect);
  355. }
  356.  
  357.  
  358. /*******************************************************************************
  359. * GetSFCurDir, GetSFVRefNum                                                    *
  360. *                                                                              *
  361. * The following set of routines are used to access a couple of low memory      *
  362. * globals that are necessary when extending Standard File.  One example is     *
  363. * trying to get the current directory while in a file filter.  These routines  *
  364. * were used to bottleneck all the low memory usage.  If the system one day     *
  365. * supports them with a trap call, then we can easily update these routines.    *
  366. *******************************************************************************/
  367.  
  368. long GetSFCurDir()
  369. {
  370.     return( *(long*)CurDirStore );
  371. }
  372.  
  373. short GetSFVRefNum()
  374. {
  375.     return( - *(short*)SFSaveDisk);
  376. }
  377.  
  378.  
  379. /*******************************************************************************
  380. * StrCpy                                                                       *
  381. *                                                                              *
  382. *     Just like strcpy except that it takes pascal strings as arguments.       *                                                                               *
  383. *******************************************************************************/
  384. char *StrCpy( char *s1, char *s2 )
  385. {
  386.     /*-------------------------------------------------------------------------+
  387.     | Copy The Length Byte                                                     |
  388.     +-------------------------------------------------------------------------*/
  389.     *s1 = *s2;
  390.  
  391.     /*-------------------------------------------------------------------------+
  392.     | Copy The Rest.                                                           |
  393.     +-------------------------------------------------------------------------*/
  394.     return( (char *)memcpy( s1+1, s2+1, *s1 ) );
  395. }
  396.  
  397.  
  398.  
  399. /*******************************************************************************
  400. * StrCat                                                                       *
  401. *                                                                              *
  402. *     Just like strcat except that it takes pascal strings as arguments.       *                                                                               *
  403. *******************************************************************************/
  404. char *StrCat( char *s1, char *s2 )
  405. {
  406.     int OriginalLen;
  407.  
  408.     OriginalLen = *s1;
  409.     *s1 += *s2;
  410.  
  411.     return( (char *)memcpy( s1+OriginalLen+1, s2+1, *s2 ) );
  412. }
  413.  
  414.  
  415.  
  416.  
  417.